import com.sun.javadoc.Doc;
import com.sun.javadoc.DocErrorReporter;
import com.sun.javadoc.LanguageVersion;
import com.sun.javadoc.ProgramElementDoc;
import com.sun.javadoc.RootDoc;
import com.sun.tools.doclets.standard.Standard;
import com.sun.tools.javadoc.Main;

import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

public class ExcludeDoclet
{
    public static void main(String[] args)
    {
        String name = ExcludeDoclet.class.getName();
        Main.execute(name, name, args);
    }

    //
    // Static Overrides of the standard doclet
    //

    public static boolean           validOptions( String[][] options, DocErrorReporter reporter )
        throws IOException
    {
        return Standard.validOptions(options, reporter);
    }

    public static int               optionLength( String option )
    {
        return Standard.optionLength( option );
    }

    public static boolean           start( RootDoc root )
        throws IOException
    {
        return Standard.start( (RootDoc)process( root, RootDoc.class ) );
    }

    public static LanguageVersion   languageVersion()
    {
        return Standard.languageVersion();
    }

    //
    // Dynamic override of RootDoc
    //

    @SuppressWarnings( "unchecked" )
    private static Object           process( final Object obj, Class expect )
    {
        if( obj == null )
            return null;

        final Class cls = obj.getClass();
        if( cls.getName().startsWith("com.sun.") )
        {
            return Proxy.newProxyInstance( cls.getClassLoader(), cls.getInterfaces(), new ExcludeHandler(obj) );
        }
        else if( obj instanceof Object[] )
        {
            final Class componentType = expect.getComponentType();
            if( componentType == null )
                return obj;

            Object[] array = (Object[])obj;
            List list = new ArrayList( array.length );
            for( final Object entry : array )
            {
                if( (entry instanceof Doc) && exclude( (Doc)entry ) )
                    continue;
                list.add( process( entry, componentType ) );
            }
            return list.toArray((Object[])Array.newInstance(componentType, list.size()));
        }
        else
        {
            return obj;
        }
    }

    //
    // Helper
    //

    private static boolean exclude( final Doc doc )
    {
        if( doc instanceof ProgramElementDoc )
        {
            if( ((ProgramElementDoc)doc).containingPackage().tags("exclude").length > 0 )
                return true;
        }
        return doc.tags("exclude").length > 0;
    }

    private static class ExcludeHandler implements InvocationHandler
    {
        private Object target;

        public ExcludeHandler( final Object target )
        {
            this.target = target;
        }

        public Object invoke( final Object proxy, final Method method, final Object[] args )
            throws Throwable
        {
            if( args != null )
            {
                final String methodName = method.getName();
                if( methodName.equals( "compareTo" ) ||
                    methodName.equals( "equals" ) ||
                    methodName.equals( "overrides" ) ||
                    methodName.equals( "subclassOf" ))
                {
                    args[0] = unwrap(args[0] );
                }
            }
            try
            {
                return process( method.invoke( target, args ), method.getReturnType() );
            }
            catch( InvocationTargetException e )
            {
                throw e.getTargetException();
            }
        }

        private Object unwrap(Object proxy)
        {
            if( proxy instanceof Proxy )
                return ((ExcludeHandler)Proxy.getInvocationHandler( proxy )).target;
            return proxy;
        }
    }
}